package org.opu.pc.ui.client.widgets;

import com.toedter.calendar.JDateChooser;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractAction;

import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.SpinnerNumberModel;

import org.opu.pc.ui.client.i18n.AbstractI18N;
import org.opu.pc.ui.util.ConfigPath;
import org.opu.pc.ui.util.GUIUtils;
import org.opu.pc.ui.util.ResourceHandle;

public class PropertiesEditPane extends JPanel {

	private static final long serialVersionUID = -8194774107425187273L;

	private Map<String, Object> properties;
	private AbstractI18N i18n;
	private Map<String /* name */, List<PropertyOption>> posibleValue;

	private Map<String, PropertyValueComponent> componentMapping =
			new HashMap<String, PropertyValueComponent>();

    private PropertyValueComponentFactory componentFactory = DEFAULT_FACTORY;

	private Dimension lblSize;
	
	public PropertiesEditPane(Map<String, Object> properties, AbstractI18N i18n) {
		this(properties, i18n, new HashMap<String, List<PropertyOption>>());
	}

	public PropertiesEditPane(Map<String, Object> properties, AbstractI18N i18n,
			Map<String, List<PropertyOption>> posibleValue) {
		
		super(new BorderLayout());
		
		this.properties = properties;
		this.i18n = i18n;
		this.posibleValue = posibleValue;

		initComponents();
	}

    public void setComponentFactory(PropertyValueComponentFactory componentFactory) {
        this.componentFactory = componentFactory;
    }

	public Map<String, Object> getProperties(){
		Map<String, Object>  prop = new HashMap<String, Object>();
		for(String name : componentMapping.keySet()){
            prop.put(name, componentMapping.get(name).getValue());
		}
		return prop;
	}

	public Dimension getLblSize() {
		return lblSize;
	}

    public void set(String name, Object value){
        componentMapping.get(name).setValue(value);
    }

	private void initComponents() {
		int size = properties.keySet().size();
		JLabel labels[] = new JLabel[size];
		JComponent texts[] = new JComponent[size];

		int i = 0;
		for (String name : properties.keySet()) {
			labels[i] = new JLabel(i18n.getSafe(name) + ": ", JLabel.RIGHT);
            PropertyValueComponent comp = componentFactory.createComponent(
                    name, i18n, properties, posibleValue);
            texts[i] = comp.asComponent();
			componentMapping.put(name, comp);
			i ++;
		}

		lblSize = initSize(labels);
		initHeight(texts);
		
		int h = GUIUtils.DEFAULT_SPACE;
		int w = GUIUtils.DEFAULT_SPACE/2;
		
		Box vb = Box.createVerticalBox();
		
		for (int j = 0; j < texts.length; j++) {
			Box hb = Box.createHorizontalBox();
			hb.add(Box.createHorizontalStrut(h));
			hb.add(labels[j]);
			hb.add(Box.createHorizontalStrut(h));
			hb.add(texts[j]);
			hb.add(Box.createHorizontalStrut(h));
			
			vb.add(Box.createVerticalStrut(w));
			vb.add(hb);
		}
		vb.add(Box.createVerticalStrut(w));
		
		add(vb);
	}

	private Dimension initSize(JComponent... components) {
		Dimension maxSiz = new Dimension(0, 0);

		for (JComponent c : components) {
			Dimension d = c.getPreferredSize();
			if (d.width > maxSiz.width) {
				maxSiz.width = d.width;
			}
			if (d.height > maxSiz.height) {
				maxSiz.height = d.height;
			}
		}

		for (JComponent c : components) {
			c.setMinimumSize(maxSiz);
			c.setPreferredSize(maxSiz);
			c.setMaximumSize(maxSiz);
		}
		
		return maxSiz;
	}

	private void initHeight(JComponent... components) {
		Dimension maxSiz = new Dimension(0, 0);

		for (JComponent c : components) {
			Dimension d = c.getPreferredSize();
			if (d.height > maxSiz.height) {
				maxSiz.height = d.height;
			}
		}

		for (JComponent c : components) {
			c.setMinimumSize(setH(c.getMaximumSize(), maxSiz.height));
			c.setPreferredSize(setH(c.getPreferredSize(), maxSiz.height));
			c.setMaximumSize(setH(c.getMaximumSize(), maxSiz.height));
		}
	}
	
	private Dimension setH(Dimension d, int h){
		d.height = h;
		return d;
	}

    public static PropertyValueComponentFactory DEFAULT_FACTORY =
            new PropertyValueComponentFactory() {

        @Override
        public PropertyValueComponent createComponent(String propName,
                AbstractI18N i18n, Map<String, Object> properties,
                Map<String, List<PropertyOption>> posibleValue) {

            if (posibleValue.containsKey(propName)) {
                PropertyOption[] array = posibleValue.get(propName).toArray(
                        new PropertyOption[] {});
                PropertyOption2[] data = new PropertyOption2[array.length];
                for (int i = 0; i < array.length; i++) {
                    data[i] = new PropertyOption2(array[i].value,
                            i18n.get(array[i].displayKey, array[i].displayDefVal));
                }
                final JComboBox box = new JComboBox(data);
                box.setSelectedIndex(0);

                return new PropertyValueComponent() {

                    @Override public JComponent asComponent() { return box; }
                    @Override public String getValue() {
                        return ((PropertyOption2)box.getSelectedItem()).id;
                    }
                    @Override public void setValue(Object o) {
                        box.setSelectedItem(new PropertyOption2("" + o, "" + o));
                    }

                };
            } else if (propName.endsWith("date")){
                final JDateChooser dateCh = new JDateChooser();
                dateCh.setLocale(ConfigPath.getInstance().getLocale());
                dateCh.setDate((Date)properties.get(propName));
                return new PropertyValueComponent() {

                    @Override public JComponent asComponent() { return dateCh; }
                    @Override public Date getValue() { return dateCh.getDate();}
                    @Override public void setValue(Object o) { dateCh.setDate((Date) o); }
                };
            } else if (propName.endsWith("number")){
                final JSpinner sp = new JSpinner(new SpinnerNumberModel(
                        ((Integer)properties.get(propName)).intValue(),
                        0, Integer.MAX_VALUE, 1));
                sp.setPreferredSize(new JTextField(10).getPreferredSize());
                return new PropertyValueComponent() {

                    @Override public JComponent asComponent() { return sp; }
                    @Override public Object getValue() { return sp.getValue();}
                    @Override public void setValue(Object o) { sp.setValue(o); }
                };
            } else if (propName.endsWith("password")){
                final JTextField tc = GUIUtils.addActions(
                        new JTextField((String)properties.get(propName), 10));

                final JPasswordField pf = GUIUtils.addActions(
                        new JPasswordField(tc.getDocument(), null, 10));

                tc.setVisible(false);

                JToggleButton shP = new JToggleButton(new ShowPswdBtnAction(tc, pf));
                shP.setMaximumSize(new Dimension(24, 24));
                shP.setPreferredSize(new Dimension(24, 24));

                final JComponent pane = Box.createHorizontalBox();
                pane.add(pf);
                pane.add(tc);
                pane.add(shP);

                return new PropertyValueComponent() {

                    @Override public JComponent asComponent() { return pane; }
                    @Override public String getValue() { return tc.getText();}
                    @Override public void setValue(Object o) {tc.setText("" + o);}
                };
            } else {
                final JTextField tc = GUIUtils.addActions(
                        new JTextField((String)properties.get(propName), 10));

                return new PropertyValueComponent() {

                    @Override public JComponent asComponent() { return tc; }
                    @Override public String getValue() { return tc.getText();}
                    @Override public void setValue(Object o) {tc.setText("" + o);}
                };
            }
        }
    };

    public static interface PropertyValueComponentFactory {

        PropertyValueComponent createComponent(String propName,
                AbstractI18N i18n, Map<String, Object> properties,
                Map<String, List<PropertyOption>> posibleValue);

    }

    public static interface PropertyValueComponent {

        JComponent asComponent();

        Object getValue();

        void setValue(Object o);
    }

	public static class PropertyOption {
		/** key for i18n value */
		public final String displayKey;
		/** if no i18n value user will see this value */
		public final String displayDefVal;
		/** Applying value */
		public final String value;

		public PropertyOption(String displayKey, String displayDefVal,
				String value) {
			this.displayKey = displayKey;
			this.displayDefVal = displayDefVal;
			this.value = value;
		}

		@Override
		public String toString() {
			return value;
		}
	}

	private static class PropertyOption2 implements Serializable{
		
		private static final long serialVersionUID = 6449821253966913135L;
		
		final String id;
		final String view;

		public PropertyOption2(String id, String view) {
			this.id = id;
			this.view = view;
		}

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final PropertyOption2 other = (PropertyOption2) obj;
            if ((this.id == null) ? (other.id != null) : !this.id.equals(other.id)) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 97 * hash + (this.id != null ? this.id.hashCode() : 0);
            return hash;
        }

		@Override
		public String toString() {
			return view;
		}
	}

    private static class ShowPswdBtnAction extends AbstractAction {
        private JTextField passwordTf;
        private JPasswordField passwordPf;

        public ShowPswdBtnAction(JTextField passwordTf, JPasswordField passwordPf) {
            super("", ResourceHandle.getIcon("showPswdBtn.png", ResourceHandle.IconType.small));
            this.passwordTf = passwordTf;
            this.passwordPf = passwordPf;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() instanceof JToggleButton){
                JToggleButton button = (JToggleButton) e.getSource();
                if (button.isSelected()){
                    passwordPf.setVisible(false);
                    passwordTf.setVisible(true);
                } else {
                    passwordPf.setVisible(true);
                    passwordTf.setVisible(false);
                }
                ((JComponent)passwordPf.getParent()).revalidate();
            }
        }
    }

}
